﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using Protocol;

namespace MDS
{
    public class RaptorMessageRepository : IMessageRepository
    {
        private RaptorDB.RaptorDB<int> _raptorDB;
        private string _dbFileName;
        private int _currentSN = -1;
        private object _lockObj = new object();

        private static Dictionary<string, RaptorMessageRepository> _allInstances = new Dictionary<string, RaptorMessageRepository>();


        private RaptorMessageRepository(string dbFileName)
        {
            Contract.Requires(!string.IsNullOrEmpty(dbFileName));
            this._dbFileName = dbFileName;
            _raptorDB = new RaptorDB.RaptorDB<int>(dbFileName, false);
            _currentSN = (int)_raptorDB.Count() - 1;
        }

        public static RaptorMessageRepository GetInstance(string dbFileName)
        {
            Contract.Requires(!string.IsNullOrEmpty(dbFileName));
            lock (_allInstances)
            {
                if (_allInstances.ContainsKey(dbFileName))
                    return _allInstances[dbFileName];
                else
                {
                    RaptorMessageRepository newInstance = new RaptorMessageRepository(dbFileName);
                    _allInstances[dbFileName] = newInstance;
                    return newInstance;
                }
            }
        }


        public void ClearAllData()
        {
            _raptorDB.Dispose();
            System.IO.File.Delete(this._dbFileName);
        }

        public int GetMessageCount()
        {
            return (int)_raptorDB.Count();
        }

        public int GetCurrentSN()
        {
            return this._currentSN;
        }

        public int AddMessage(Protocol.IMessage message)
        {
            Contract.Requires(message != null);
            dynamic msgDynamic = message;

            lock (this._lockObj)
            {
                int sequenceNo = (int)this._currentSN + 1;
                msgDynamic.SequenceNo = sequenceNo;
                byte[] data = MessageSerializer.Serialize(message);
                this._raptorDB.RemoveKey(sequenceNo);
                this._raptorDB.Set(sequenceNo, data);
                this._currentSN = sequenceNo;
                return sequenceNo;
            }
        }


        public int AddMessages(IEnumerable<Protocol.IMessage> messages)
        {
            Contract.Requires(messages != null);
            List<Protocol.IMessage> msgList = messages.ToList();
            lock (this._lockObj)
            {
                int currentSN = (int)this._currentSN;
                int sequenceNo = this._currentSN;

                foreach (var message in msgList)
                {
                    dynamic msgDynamic = message;
                    sequenceNo = this._currentSN + 1;
                    msgDynamic.SequenceNo = sequenceNo;
                    byte[] data = MessageSerializer.Serialize(message);
                    try
                    {
                        this._raptorDB.Set(sequenceNo, data);
                    }
                    catch (Exception ex)
                    {
                        bool rollbackSucceed = true;
                        for (int i = currentSN; i < sequenceNo; i++)
                        {
                            try
                            {
                                this._raptorDB.RemoveKey(i);
                            }
                            catch (Exception ex2)
                            {
                                rollbackSucceed = false;
                            }
                        }
                        sequenceNo = currentSN;
                        if (rollbackSucceed)
                            throw new Exception("插入失败，回滚成功", ex);
                        else
                            throw new Exception("插入失败，回滚失败", ex);
                    }
                }
                this._currentSN = sequenceNo;
                return sequenceNo;

            }
        }



        public IEnumerable<Protocol.IMessage> GetMessages(int beginSN, int endSN)
        {
            Contract.Requires(beginSN >= 0);
            Contract.Requires(endSN > beginSN);
            List<Protocol.IMessage> rtn = new List<Protocol.IMessage>();
            for (int currentSN = beginSN; currentSN <= endSN; currentSN++)
            {
                byte[] data;
                bool readSucceed = this._raptorDB.Get(beginSN, out data);
                if (readSucceed)
                {
                    Protocol.IMessage msg;
                    bool deserializeSucceed = MessageSerializer.TryDeserialize(data, out msg);
                    if (deserializeSucceed)
                        rtn.Add(msg);
                }
            }
            return rtn;
        }
        public IMessage GetMessage(int sequenceNo)
        {
            Contract.Requires(sequenceNo >= 0);
            byte[] data;
            bool readSucceed = this._raptorDB.Get(sequenceNo, out data);
            if (readSucceed)
            {
                Protocol.IMessage msg;
                bool deserializeSucceed = MessageSerializer.TryDeserialize(data, out msg);
                if (deserializeSucceed)
                    return msg;
                else
                    return null;
            }
            else
            {
                return null;
            }
        }










      
    }
}
